package com.hmall.user.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.exception.ForbiddenException;
import com.hmall.common.utils.UserContext;
import com.hmall.user.config.JwtProperties;
import com.hmall.user.domain.dto.LoginFormDTO;
import com.hmall.user.domain.po.User;
import com.hmall.user.domain.vo.UserLoginVO;
import com.hmall.user.enums.UserStatus;
import com.hmall.user.mapper.UserMapper;
import com.hmall.user.service.IUserService;
import com.hmall.user.utils.JwtTool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;


/**
 * 用户业务实现类
 * 继承自MyBatis-Plus的ServiceImpl，提供基础CRUD功能
 * 实现IUserService接口，处理用户登录、余额扣款等核心业务逻辑
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    // 密码编码器（用于校验密码）
    private final PasswordEncoder passwordEncoder;

    // JWT工具（用于生成和解析Token）
    private final JwtTool jwtTool;

    // JWT配置（获取Token有效期等配置）
    private final JwtProperties jwtProperties;

    /**
     * 用户登录业务逻辑
     */
    @Override
    public UserLoginVO login(LoginFormDTO loginDTO) {

        // 1. 从登录表单中获取用户名和密码
        String username = loginDTO.getUsername();
        String password = loginDTO.getPassword();

        // 2. 根据用户名查询用户信息
        User user = lambdaQuery()
                .eq(User::getUsername, username)
                .one();

        // 校验用户是否存在
        Assert.notNull(user, "用户名错误");

        // 3. 校验用户状态（是否被冻结）
        if (user.getStatus() == UserStatus.FROZEN) {
            throw new ForbiddenException("用户被冻结");
        }

        // 4. 校验密码（使用Spring Security的密码编码器）
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new BadRequestException("用户名或密码错误");
        }

        // 5. 生成JWT Token（有效期从配置中获取）
        String token = jwtTool.createToken(
                user.getId(),
                jwtProperties.getTokenTTL()
        );

        // 6. 封装返回结果（包含用户ID、用户名、余额和Token）
        UserLoginVO vo = new UserLoginVO();
        vo.setUserId(user.getId());
        vo.setUsername(user.getUsername());
        vo.setBalance(user.getBalance());
        vo.setToken(token);

        return vo;
    }

    /**
     * 账户余额扣款业务逻辑
     */
    @Override
    public void deductMoney(String pw, Integer totalFee) {

        log.info("开始扣款，用户ID：{}，金额：{}", UserContext.getUser(), totalFee);

        // 1. 获取当前登录用户信息
        // 拦截器会在扣款Http请求处理前，从请求头中提取用户信息并存入到ThreadLocal中，实现同请求内多组件共享用户上下文
        User user = getById(UserContext.getUser());

        // 校验用户和支付密码
        if (user == null || !passwordEncoder.matches(pw, user.getPassword())) {
            throw new BizIllegalException("用户密码错误");
        }

        // 2. 尝试扣款（调用自定义Mapper方法）
        try {
            baseMapper.updateMoney(UserContext.getUser(), totalFee);
        } catch (Exception e) {

            // 捕获异常，可能是余额不足或数据库错误
            log.error("扣款失败，用户ID：{}", UserContext.getUser(), e);
            throw new RuntimeException("扣款失败，可能是余额不足！", e);
        }

        log.info("扣款成功，用户ID：{}，金额：{}", UserContext.getUser(), totalFee);
    }
}
